package org.gikolet.util;

import java.util.Arrays;

public final class Tuple {

	private Tuple() {
	}

	public static class Tuple1<V1> {

		public final V1 _1;

		public Tuple1(V1 _1) {
			this._1 = _1;
		}

		public final V1 get1() {
			return _1;
		}

		@SuppressWarnings("unchecked")
		public boolean equals(Object obj) {
			if (obj == null) {
				return false;
			}
			if (obj instanceof Tuple1) {
				Object[] that = getValues((Tuple1<V1>) obj);
				return Arrays.deepEquals(that, getValues(this));
			}
			return false;
		}

		public int hashCode() {
			return Arrays.deepHashCode(getValues(this));
		}

		private static <V1> Object[] getValues(Tuple1<V1> v) {
			return new Object[] { v._1 };
		}

		@Override
		public String toString() {
			return new StringBuilder().append(this._1).toString();
		}
	}

	public static class Tuple2<V1, V2> extends Tuple1<V1> {

		public final V2 _2;

		public Tuple2(V1 _1, V2 _2) {
			super(_1);
			this._2 = _2;
		}

		public final V2 get2() {
			return _2;
		}

		@SuppressWarnings("unchecked")
		public boolean equals(Object obj) {
			if (obj == null) {
				return false;
			}
			if (obj instanceof Tuple2) {
				Object[] that = getValues((Tuple2<V1, V2>) obj);
				return Arrays.deepEquals(that, getValues(this))
						&& super.equals(obj);
			}
			return false;
		}

		public int hashCode() {
			return Arrays.deepHashCode(getValues(this)) + super.hashCode() * 37;
		}

		private static <V1, V2> Object[] getValues(Tuple2<V1, V2> v) {
			return new Object[] { v._2 };
		}

		@Override
		public String toString() {
			return new StringBuilder().append(super.toString()).append(",")
					.append(this._2).toString();
		}
	}

	public static class Tuple3<V1, V2, V3> extends Tuple2<V1, V2> {

		public final V3 _3;

		public Tuple3(V1 _1, V2 _2, V3 _3) {
			super(_1, _2);
			this._3 = _3;
		}

		public final V3 get3() {
			return _3;
		}

		@SuppressWarnings("unchecked")
		public boolean equals(Object obj) {
			if (obj == null) {
				return false;
			}
			if (obj instanceof Tuple3) {
				Object[] that = getValues((Tuple3<V1, V2, V3>) obj);
				return Arrays.deepEquals(that, getValues(this))
						&& super.equals(obj);
			}
			return false;
		}

		public int hashCode() {
			return Arrays.deepHashCode(getValues(this)) + super.hashCode() * 37;
		}

		private static <V1, V2, V3> Object[] getValues(Tuple3<V1, V2, V3> v) {
			return new Object[] { v._3 };
		}

		@Override
		public String toString() {
			return new StringBuilder().append(super.toString()).append(",")
					.append(this._3).toString();
		}
	}

	public static class Tuple4<V1, V2, V3, V4> extends Tuple3<V1, V2, V3> {

		public final V4 _4;

		public Tuple4(V1 _1, V2 _2, V3 _3, V4 _4) {
			super(_1, _2, _3);

			this._4 = _4;
		}

		public final V4 get4() {
			return _4;
		}

		@SuppressWarnings("unchecked")
		public boolean equals(Object obj) {
			if (obj == null) {
				return false;
			}
			if (obj instanceof Tuple4) {
				Object[] that = getValues((Tuple4<V1, V2, V3, V4>) obj);
				return Arrays.deepEquals(that, getValues(this))
						&& super.equals(obj);
			}
			return false;
		}

		public int hashCode() {
			return Arrays.deepHashCode(getValues(this)) + super.hashCode() * 37;
		}

		private static <A, B, C, D> Object[] getValues(Tuple4<A, B, C, D> v) {
			return new Object[] { v._4 };
		}

		@Override
		public String toString() {
			return new StringBuilder().append(super.toString()).append(",")
					.append(this._4).toString();
		}
	}
}
